
//#define MINESWEEPER_RES 0x70000
//after tetris
#define MINESWEEPER_RES 0x00003200
#define IMG_ARR 0
#define IMG_SIZE (16*16*2)
#define IMG_CNT (10)
#define MA_ARR (IMG_ARR + IMG_SIZE*IMG_CNT)
#define MA_SIZE (16*16*2)
#define MA_CNT (5)
#define NUM_ARR (MA_ARR + MA_SIZE*MA_CNT)
#define NUM_SIZE (12*23*2)
#define NUM_CNT (10)
#define MI_ARR (NUM_ARR + NUM_SIZE*NUM_CNT)
#define MI_SIZE (26*26*2)

#define boardW 30
#define boardH 16

static int res;

class Panel : public Window{
  public:
  int downx;
  int downy;
  int down;
  void init(){
    Window::init();
    width = boardW * 16;
    height = boardH * 16;
    regDraw(Panel::draw);
    regMouseDown(Panel::mouseDown);
    regMouseUp(Panel::mouseUp);
  }
  void draw2(int x0, int x1, int y0, int y1);
    
  static void draw(Window* win){
    Panel* self = (Panel*)win;
    self->draw2(0, boardW, 0, boardH);
  }
  static void mouseDown(Window* win, int x, int y, int btn);
  static void mouseUp(Window* win, int x, int y, int btn);

};



void FormMineSweeper_close(Window*);
void FormMineSweeper_dispose(Window* self);
void FormMineSweeper_draw(Window* self);
void onMineSweeperTimer(Timer_win* timer);
void Button_mine_reset(Button* button);
void Button_draw(Button* button);
void Button_mouseEvent(Window* w, int x, int y, int e);

int toBcd(int val)
{
  int idx;
  int i;
  int num;
  if(val>=0){
    num = val;
  }else{
    return 0;
  }
  
  int bcd = 0;
  int outNum;
  int flg = 0;
  for(idx = 2; idx < 10 ; idx++)
  {
    outNum = 0;
    int CmpNum = num10s[idx];
    for(i = 0; i < 10 ; i++)
    {
      if(num>=CmpNum)
      {
        num -= CmpNum;
        outNum++;
      }
      else
      {
        break;
      }
    }
    bcd|=outNum<<((32-(idx-1))<<2);
  }
  return bcd;
}

class FormMineSweeper : public Form
{
  public:

  int autoLevel; //0 no open, 1 open empty, 2: ai
  int mineBoard[boardW*boardH];
  int status[boardW*boardH];//0x00:close  0x10:open  0x01:flag mark  0x02:question mark

  int isFailed;
  int isWined;

  int imgArr[10];
  int num[10];
  int ma[5];//flag, question mark, red mine, x mine, mine
  int mi[5];

  Timer_win* timer;
  Panel* panel;

  int time;
  int mineCount;

  void drawNum(int bx, int by, int val){
    
    int bcd = toBcd(val);
    drawImgWin((void *)(res + MINESWEEPER_RES + NUM_ARR + NUM_SIZE*((bcd>>0)&0x0F)), bx+12*2, by, 12, 23);
    drawImgWin((void *)(res + MINESWEEPER_RES + NUM_ARR + NUM_SIZE*((bcd>>4)&0x0F)), bx+12*1, by, 12, 23);
    drawImgWin((void *)(res + MINESWEEPER_RES + NUM_ARR + NUM_SIZE*((bcd>>8)&0x0F)), bx+12*0, by, 12, 23);
  }

  int inRange(int x, int y){
    if(x>=0 && x<boardW && y>=0 && y<boardH){
      return 1;
    }else{
      return 0;
    }
  }

  int getMine(int x, int y){
    if(inRange(x, y)){
      return mineBoard[x+y*boardW];
    }else{
      return 0;
    }
  }

  int getCount(int x, int y){
    int cnt = 0;
    cnt = cnt + getMine((x+1),(y+1));
    cnt = cnt + getMine((x  ),(y+1));
    cnt = cnt + getMine((x-1),(y+1));
    cnt = cnt + getMine((x+1),(y  ));
    cnt = cnt + getMine((x  ),(y  ));
    cnt = cnt + getMine((x-1),(y  ));
    cnt = cnt + getMine((x+1),(y-1));
    cnt = cnt + getMine((x  ),(y-1));
    cnt = cnt + getMine((x-1),(y-1));
    return cnt;
  }

  inline int getStatus(int x, int y){
    return status[x+y*boardW];
  }
  void setStatus(int x, int y, int val){
    status[x+y*boardW] = val;
  }



  int isWin(){
    int notyet=0;
    
    for(int j=0;j<boardH;j++){
      for(int i=0;i<boardW;i++){
        if(getMine(i, j)==0){
          if(getStatus(i, j)==0x10){//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
          }else{
            notyet = 1;
          }
        }
      }
    }
    
    if(!notyet){
      return 1;
    }else{
      return 0;
    }
  }

  void open(int x, int y){
    if(inRange(x, y)){
      if(getStatus(x, y)==0){//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
        setStatus(x, y, 0x10);
        if(isWin()){
          isWined = 1;
        }else{
          if(getMine(x, y)){
            isFailed = 1;
            this->needRefresh = 1;
          }else{
            int cnt = getCount(x, y);
            panel->drawImgWin((void *)(res + MINESWEEPER_RES + IMG_ARR + IMG_SIZE*(cnt)), x*16, y*16, 16, 16);//open
            if(cnt==0){
              open(x+1, y+1);
              open(x  , y+1);
              open(x-1, y+1);
              open(x+1, y  );
              //open(x  , y  );
              open(x-1, y  );
              open(x+1, y-1);
              open(x  , y-1);
              open(x-1, y-1);
            }
          }
        }
        //AI
      }
    }
  }

  void reset(){
    int total = boardW*boardH;
    isFailed = 0;
    isWined = 0;
    time = 123;
    for(int i=0;i<total;i++){
      mineBoard[i] = 0;
    }
    for(int i=0;i<mineCount;i++){
      mineBoard[i] = 1;
    }
    
    for(int i=0;i<total;i++){
      int pos = i+rnd(total-i);
      int tmp = mineBoard[pos];
      mineBoard[pos] = mineBoard[i];
      mineBoard[i] = tmp;
    }
    
    for(int i=0;i<total;i++){
      status[i] = 0;
    }
    this->needRefresh = 1;
  }

  int rnd(int max){
    int v = ::rnd() & 0xFFFF;
    v = v * max;
    v = v >> 16;
    return v;
  }

  void onclick(int x, int y, int b1, int b2){
  }

  void onTimer() {
    if(isFailed || isWined)return;
    time++;
    drawNum(448, 16+22, time);
  }

  void init(){
    Form::init("minesweeper");
    mineCount = 20;
    time = 567;

    {
      Button* b = new Button();
      b->init("20");
      b->id=1;
      b->pos_x = 100; b->pos_y = 16+22; b->width = 26;
      b->regClick(Button_mine_reset);
      addChild(b);
    }
    {
      Button* b = new Button();
      b->init("50");
      b->id=2;
      b->pos_x = 140; b->pos_y = 16+22;  b->width = 26;
      b->regClick(Button_mine_reset);
      addChild(b);
    }
    {
      Button* b = new Button();
      b->init("99");
      b->id=3;
      b->pos_x = 180; b->pos_y = 16+22;  b->width = 26;
      b->regClick(Button_mine_reset);
      addChild(b);
    }

    {
      Button* b = new Button();
      b->init(0);
      b->id=4;
      b->pos_x = 240; b->pos_y = 16+22;  b->width = 25;  b->height = 25;
      b->regDraw(Button_draw);
      b->regClick(Button_mine_reset);
      addChild(b);
    }

    width = 509;
    height = 351;
    pos_x = 50;
    pos_y = 20;
    //b->text="timer";
    color = getRGB565(0x0000000);
    regClose(FormMineSweeper::close);
    regDispose(FormMineSweeper::dispose);
    regDraw(FormMineSweeper::draw);
    
    res = (int)__getRes__();
    int posx = 0;
    int index = 0;

    {
      timer = new Timer_win();
      timer->interval = 1000;//ms
      timer->nextTick = 0;//ms
      timer->pOnTime = onMineSweeperTimer;
      ::windows->addTimer(timer);
    }
    
    {
      panel = new Panel();
      panel->init();
      panel->id = 105;
      panel->pos_x = 13;
      panel->pos_y = 57+22;
      addChild(panel);
    }
    
    reset();
  }
  static void close(Form* form){
    // windows->removeTimer(formMineSweeper->timer);
    // windows->removeWindow((Window*)formMineSweeper);
    // windows->refresh();
    // formMineSweeper=0;
  }

  static void dispose(Window* form){
    //panel
    // free(((FormMineSweeper*)self)->timer);
    // ((FormMineSweeper*)self)->dispose();
  }
  
  static void draw(Form* form){
    FormMineSweeper* self = (FormMineSweeper*)form;
    self->paintWin(0+502-3 + 2,        0 + 24,     3,   322, getColorRGB(255, 255, 255), 0);
    self->paintWin(      0 + 2,  0+322-3 + 24,   502,     3, getColorRGB(255, 255, 255), 0);
    self->paintWin(      0 + 2,        0 + 24,   502,     3, getColorRGB(255, 255, 255), 0);
    self->paintWin(      0 + 2,        0 + 24,     3,   322, getColorRGB(255, 255, 255), 0);
    self->paintWin(      3 + 2,        3 + 24, 502-6, 322-6, getColorRGB(192, 192, 192), 0);
    self->paintWin(      9 + 2,        9 + 24,   486,     2, getColorRGB(128, 128, 128), 0);
    self->paintWin(      9 + 2,        9 + 24,     2,    37, getColorRGB(128, 128, 128), 0);
    self->paintWin(9+486-2 + 2,        9 + 24,     2,    37, getColorRGB(255, 255, 255), 0);
    self->paintWin(      9 + 2,   9+37-2 + 24,   486,     2, getColorRGB(255, 255, 255), 0);
    self->paintWin(      9 + 2,       53 + 24,   485,     2, getColorRGB(128, 128, 128), 0);
    self->paintWin(      9 + 2,       53 + 24,     2,   261, getColorRGB(128, 128, 128), 0);
    self->paintWin(9+485-2 + 2,       53 + 24,     2,   261, getColorRGB(255, 255, 255), 0);
    self->paintWin(      9 + 2, 53+261-2 + 24,   485,     2, getColorRGB(255, 255, 255), 0);

    
    self->drawNum(448, 16+22, self->time);
    self->drawNum(18, 16+22, self->mineCount);

  }
    
};

void onMineSweeperTimer(Timer_win* timer) {
  formMineSweeper->onTimer();
}

void Button_mine_reset(Button* button){
  if(button->id==1){
    formMineSweeper->mineCount = 20;
  }else if(button->id==2){
    formMineSweeper->mineCount = 50;
  }else if(button->id==3){
    formMineSweeper->mineCount = 99;
  }
  formMineSweeper->reset();
}

void Button_draw(Button* button){
  FormMineSweeper* f = formMineSweeper;
  if(button->isDown){
    button->drawImgWin((void *)(res + MINESWEEPER_RES + MI_ARR + MI_SIZE*(4)), 0, 0, 26, 26);//down
  } else {
    //3: o
    if(f->isWined){
      button->drawImgWin((void *)(res + MINESWEEPER_RES + MI_ARR + MI_SIZE*(2)), 0, 0, 26, 26);//wined
    }else if(f->isFailed){
      button->drawImgWin((void *)(res + MINESWEEPER_RES + MI_ARR + MI_SIZE*(1)), 0, 0, 26, 26);//failed
    }else{
      button->drawImgWin((void *)(res + MINESWEEPER_RES + MI_ARR + MI_SIZE*(0)), 0, 0, 26, 26);//normal
    }
  }
}

void Panel::draw2(int x0, int x1, int y0, int y1){

  int isFinish = formMineSweeper->isFailed || formMineSweeper->isWined;


  for(int j=y0;j<y1;j++){
    for(int i=x0;i<x1;i++){
      int status = formMineSweeper->getStatus(i, j);
      int mine = formMineSweeper->getMine(i, j);
      switch(status){//      
      case 0x00://0x00:close
        drawImgWin((void *)(res + MINESWEEPER_RES + IMG_ARR + IMG_SIZE*(9)), i*16, j*16, 16, 16);//close
        break;
      case 0x10://0x10:open
        if(mine){
          drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(4)), i*16, j*16, 16, 16);//mine
        }else{
          int cnt = formMineSweeper->getCount(i, j);
          drawImgWin((void *)(res + MINESWEEPER_RES + IMG_ARR + IMG_SIZE*(cnt)), i*16, j*16, 16, 16);//open
        }
        break;
      case 0x01://0x01:flag mark
        if(isFinish){
          if(mine){
            drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(4)), i*16, j*16, 16, 16);//mine
          }else{
            drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(3)), i*16, j*16, 16, 16);//x mine
          }
        }else{
          drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(0)), i*16, j*16, 16, 16);//flag
        }
        break;
      case 0x02://0x02:question mark
        if(isFinish){
          if(mine){
            drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(4)), i*16, j*16, 16, 16);//mine
          }else{
            drawImgWin((void *)(res + MINESWEEPER_RES + IMG_ARR + IMG_SIZE*(0)), i*16, j*16, 16, 16);//open
          }
        }else{
          drawImgWin((void *)(res + MINESWEEPER_RES + MA_ARR + MA_SIZE*(1)), i*16, j*16, 16, 16);//flag
        }
        break;
      }
    }
  }
  
}

void Panel::mouseDown(Window* win, int x, int y, int btn){
  Panel* self = (Panel*)win;
  if(formMineSweeper->isFailed || formMineSweeper->isWined)return;

  x = x / 16;
  y = y / 16;
  if(btn==1){
    self->downx = x;
    self->downy = y;
    self->down = 1;
  }
  if(btn==2){
    int status = formMineSweeper->getStatus(x, y);//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
    if(status==0){
      formMineSweeper->setStatus(x, y, 0x01);
      formMineSweeper->mineCount--;
    }
    if(status==0x01){
      formMineSweeper->setStatus(x, y, 0x02);
      formMineSweeper->mineCount++;
    }
    if(status==0x02){
      formMineSweeper->setStatus(x, y, 0x00);
    }
  }
  self->draw2(x, x + 1, y, y + 1);
}

void Panel::mouseUp(Window* win, int x, int y, int btn){
  Panel* self = (Panel*)win;
  if(btn==1){
    if(self->down==1){
      formMineSweeper->open(self->downx, self->downy);
      self->down = 0;
    }
  }
  self->draw2(x, x + 1, y, y + 1);
}